package sso.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import sso.util.JwtUtils;
import sso.util.WebUtils;

import java.util.*;

/**构建配置安全对象*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**密码加密*/
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    };

    /**
     * 定义认证规则
     * @param http 安全对象
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.关闭跨域攻击
        http.csrf().disable();
        //2.配置form认证
        http.formLogin()
                .successHandler(successHandler()) //登录成功
                .failureHandler(failureHandler());//登录失败
        http.exceptionHandling()
                .authenticationEntryPoint(entryPoint()); //提示要认证
        //3.所有资源都要认证
        http.authorizeRequests().anyRequest().authenticated();
    }
    /**登录成功以后的处理器*/
    private AuthenticationSuccessHandler successHandler(){
        //当接口中只有一个抽象方法时,构建接口的实现类对象,可以参考如下写法:
        return (request,response,authentication) -> {
            Map<String,Object> map=new HashMap<>();
            map.put("state",200);
            map.put("message","login ok");
            //思考除了返回这些信息,还要返回什么?(JWT令牌,令牌中要存什么)
            Map<String,Object> userInfo=new HashMap<>();
            //获取用户对象,此对象为登录成功以后封装了登录信息的对象
            User user = (User) authentication.getPrincipal();
            //获取用户名,并装到userInfo中
            userInfo.put("username",user.getUsername());//登录用户
            //获取用户权限封装到userInfo中
            List<String> authorities=new ArrayList<>();
            Collection<GrantedAuthority> as = user.getAuthorities();
            for(GrantedAuthority a:as){
                authorities.add(a.getAuthority());
            }
//          user.getAuthorities().forEach((authority)->{ //新玩法
//                authorities.add(authority.getAuthority());//sys:res:create
//          });
            userInfo.put("authorities",authorities);//[sys:res:create,sys:res:retrieve]
            //创建token
            String token=JwtUtils.generatorToken(userInfo);
            map.put("token",token);
            WebUtils.writeJsonToClient(response,map);
        };
    }
    /**登录失败的处理器*/
    private AuthenticationFailureHandler failureHandler(){
        return (request,response,exception)->{
            Map<String,Object> map=new HashMap<>();
            map.put("state",500);
            map.put("message","username or password error");
            //思考除了返回这些信息,还要返回什么?(JWT令牌)
            WebUtils.writeJsonToClient(response,map);
        };
    }
    /**假如没有登录访问资源时给出提示*/
    private AuthenticationEntryPoint entryPoint(){
        return (request,response,exception)->{
            Map<String,Object> map=new HashMap<>();
            map.put("state",500);
            map.put("message","please login");
            //思考除了返回这些信息,还要返回什么?(JWT令牌)
            WebUtils.writeJsonToClient(response,map);
        };
    }
}

